#include "StdAfx.hpp"
#include "ParamFramework.hpp"


#if 0

namespace paramframe
{

const char * const ATTR_TYPE_NAMES[] = {
	"null",
	"object",
	"char",
	"string",
	"bool",
	"uint8", "uint16", "uint32", "uint64",
	"int8", "int16", "int32", "int64",
	"float32", "float64",
	NULL
};

static const struct {
	const char *AltName;
	ATTR_TYPE Type;
} ATTR_ALT_NAMES[] = {
	{ "int", ATTR_TYPE_INT32 },
	{ "uint", ATTR_TYPE_UINT32 },
	{ "float", ATTR_TYPE_FLOAT32 },
	{ "double", ATTR_TYPE_FLOAT64 },
};

//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class NameIdManager

NameIdManager g_NameIdManager;

NameIdManager::NameIdManager()
: m_NextId(1)
{

}

NameIdManager::~NameIdManager()
{

}

uint NameIdManager::NameToId( const char *name )
{
	return NameToId(string(name));
}

uint NameIdManager::NameToId( const string &name )
{
	if (name.empty())
		return 0;

	MapType::iterator it = m_Map.find(name);
	if (it == m_Map.end())
	{
		it = m_Map.insert(MapType::value_type(name, m_NextId++)).first;
		m_RevMap.insert(RevMapType::value_type(it->second, name));
	}
	return it->second;
}

const std::string & NameIdManager::IdToName(uint id)
{
	if (id == 0)
		return EMPTY_STRING;

	RevMapType::iterator it = m_RevMap.find(id);
	if (it == m_RevMap.end())
		return EMPTY_STRING;
	else
		return it->second;
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class Attribute

Attribute::Attribute( const std::string &name, uint minCount, uint maxCount, uint flags )
: m_NameId(g_NameIdManager.NameToId(name))
, m_Offset(0)
, m_MinCount(minCount)
, m_MaxCount(maxCount)
, m_Flags(flags)
{
	assert(m_MaxCount > 0 && m_MaxCount >= m_MinCount);
	assert((flags & FLAG_DYNAMIC) || m_MaxCount < 1000000);
}

void Attribute::Hash(common::Hash_Calc &calc)
{
	ATTR_TYPE type = GetType();

	calc.Write(&m_NameId, sizeof(m_NameId));
	calc.Write(&m_Offset, sizeof(m_Offset));
	calc.Write(&type, sizeof(type));
	calc.Write(&m_MinCount, sizeof(m_MinCount));
	calc.Write(&m_MaxCount, sizeof(m_MaxCount));
	calc.Write(&m_Flags, sizeof(m_Flags));
	ExtraHash(calc);
}

uint Attribute::GetSize()
{
	uint valueSize = GetValueSize();
	if (valueSize == 0)
		return 0;
	
	// Fixed count
	if (m_MinCount == m_MaxCount)
	{
		if (m_Flags & FLAG_DYNAMIC)
			return sizeof(void*); // Pointer to dynamic memory
		else
			return valueSize * m_MinCount; // static array
	}
	// Variable count
	else
	{
		if (m_Flags & FLAG_DYNAMIC)
			return sizeof(uint) + sizeof(void*); // Count + pointer to dynamic memory
		else
			return sizeof(uint) + valueSize * m_MaxCount; // Count + static array of max capacity
	}
}

void * Attribute::AccessValue( Object *obj, uint index )
{
	assert(index < GetValueCount(obj));

	int offset = m_Offset;
	// Variable count - skip counter
	if (m_MaxCount != m_MinCount)
		offset += sizeof(uint);
	
	// Dynamic
	if (m_Flags & FLAG_DYNAMIC)
	{
		char *ptr = *(char**)(&obj->m_Data[0] + offset);
		int offset2 = 0;
		if (index > 0)
			offset2 = index * GetValueSize();
		return ptr + offset2;
	}
	// Static
	else
	{
		if (index > 0)
			offset += index * GetValueSize();
		return &obj->m_Data[0] + offset;
	}
}

const void * Attribute::AccessValue( const Object *obj, uint index )
{
	// Just not to copy contents of the method above.
	return AccessValue(const_cast<Object*>(obj), index);
}

const std::string & Attribute::GetName()
{
	return g_NameIdManager.IdToName(m_NameId);
}

uint Attribute::GetValueCount( const Object *obj )
{
	// Constant count - no counter
	if (m_MinCount == m_MaxCount)
		return m_MinCount;
	// Variable count - access counter
	else
		return *(uint*)(&obj->m_Data[0] + m_Offset);
}

void Attribute::InitObject( char *objPtr )
{
	char *ptr = objPtr + m_Offset;
	uint count = m_MinCount;
	
	// Variable count - set count
	if (m_MaxCount != m_MinCount)
	{
		*(uint*)ptr = count;
		ptr += sizeof(uint);
	}

	uint valueSize = MAXUINT4;
	if (count > 0)
		valueSize = GetValueSize();

	if (m_Flags & FLAG_DYNAMIC)
	{
		char *dynPtr = NULL;
		if (count > 0)
			dynPtr = new char[valueSize * count];
		*(char**)ptr = dynPtr;
		ptr = dynPtr;
	}

	for (uint i = 0; i < count; i++)
	{
		InitValue(ptr);
		ptr += valueSize;
	}
}

void Attribute::FinalizeObject( char *objPtr )
{
	char *ptr = objPtr + m_Offset;
	
	uint count;
	if (m_MaxCount == m_MinCount)
		count = m_MinCount;
	else
	{
		count = *(const uint*)ptr;
		ptr += sizeof(const uint*);
	}

	uint valueSize = MAXUINT4;
	if (count > 0)
		valueSize = GetValueSize();

	char *valuesPtr;
	if (m_Flags & FLAG_DYNAMIC)
		valuesPtr = *(char**)ptr;
	else
		valuesPtr = ptr;

	char *valuePtr = valuesPtr;
	for (uint i = 0; i < count; i++)
	{
		DeinitValue(valuePtr);
		valuePtr += valueSize;
	}

	if (m_Flags & FLAG_DYNAMIC)
		delete [] valuesPtr;
}

void Attribute::SetValueCount( Object *obj, uint newCount )
{
	assert(newCount >= m_MinCount && newCount <= m_MaxCount);
	if (m_MinCount == m_MaxCount)
		return;

	char *objPtr = &obj->m_Data[0];
	char *attrPtr = objPtr + m_Offset;
	uint *countPtr = (uint*)attrPtr;
	attrPtr += sizeof(uint);
	uint oldCount = *countPtr;
	if (newCount == oldCount)
		return;

	uint valueSize = GetValueSize();
	
	if (m_Flags & FLAG_DYNAMIC)
	{
		char **dynMemPtr = (char**)attrPtr;
		char *newValuesPtr, *oldValuesPtr = *dynMemPtr;
		
		if (newCount == 0)
			newValuesPtr = NULL;
		else if (newCount < oldCount && newCount >= oldCount/2)
			newValuesPtr = oldValuesPtr;
		else
			newValuesPtr = new char[newCount * valueSize];

		// Move common values
		char *oldValuePtr = oldValuesPtr, *newValuePtr = newValuesPtr;
		if (newValuesPtr != oldValuesPtr)
		{
			uint reallocCount = std::min(oldCount, newCount);
			for (uint i = 0; i < reallocCount; i++)
			{
				CopyInitValue(newValuePtr, oldValuePtr);
				DeinitValue(oldValuePtr);
				oldValuePtr += valueSize;
				newValuePtr += valueSize;
			}
		}

		// Initialize new values
		if (newCount > oldCount)
		{
			for (uint i = oldCount; i < newCount; i++)
			{
				InitValue(newValuePtr);
				newValuePtr += valueSize;
			}
		}
		// Deinitialize deleted values
		else
		{
			for (uint i = newCount; i < oldCount; i++)
			{
				DeinitValue(oldValuePtr);
				oldValuePtr += valueSize;
			}
		}

		// Free old dynamic memory block
		if (newValuesPtr != oldValuesPtr)
		{
			*dynMemPtr = newValuesPtr;
			delete [] oldValuesPtr;
		}
	}
	else
	{
		char *valuePtr = attrPtr;
		if (newCount > oldCount)
		{
			valuePtr += oldCount * valueSize;
			for (uint i = oldCount; i < newCount; i++)
			{
				InitValue(valuePtr);
				valuePtr += valueSize;
			}
		}
		else
		{
			valuePtr += newCount * valueSize;
			for (uint i = newCount; i < oldCount; i++)
			{
				DeinitValue(valuePtr);
				valuePtr += valueSize;
			}
		}
	}

	*countPtr = newCount;
}

bool Attribute::ValidateObject( char *objPtr, const char **outErrMsg )
{
	if (outErrMsg)
		*outErrMsg = NULL;

	char *ptr = objPtr + m_Offset;
	uint count = m_MinCount;
	
	// Variable count - set count
	if (m_MaxCount != m_MinCount)
	{
		*(uint*)ptr = count;
		ptr += sizeof(uint);
	}

	uint valueSize = MAXUINT4;
	if (count > 0)
		valueSize = GetValueSize();

	if (m_Flags & FLAG_DYNAMIC)
	{
		char *dynPtr = NULL;
		if (count > 0)
			dynPtr = new char[valueSize * count];
		*(char**)ptr = dynPtr;
		ptr = dynPtr;
	}

	for (uint i = 0; i < count; i++)
	{
		if (!ValidateValue(ptr, outErrMsg))
			return false;
		ptr += valueSize;
	}

	return true;
}

bool Attribute::ValidateObject( Object &obj, const char **outErrMsg )
{
	return ValidateObject(&obj.m_Data[0], outErrMsg);
}


//void Attribute::LoadFromString( void *obj, const std::string &s, int extraOffset /*= 0*/ )
//{
//	Tokenizer tokenizer(&s, Tokenizer::FLAG_MULTILINE_STRINGS);
//	LoadText(obj, tokenizer, extraOffset);
//}
//
//void Attribute::SaveToString( const void *obj, std::string &out, int extraOffset /*= 0*/ )
//{
//	TokenWriter writer(&out);
//	SaveText(obj, writer, extraOffset);
//}

//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class Class

Class::Class(Context &context, const string &name, uint flags)
: m_Context(context)
, m_NameId(g_NameIdManager.NameToId(name))
, m_Flags(flags)
, m_State(STATE_BASE_CLASSES)
, m_Size(0)
, m_HashCalculated(false)
, m_Hash(0)
, m_ObjectCount(0)
{
	m_Context.ClassCtor(this);
}

Class::~Class()
{
	assert(m_ObjectCount == 0);
	for (uint i = m_Attributes.size(); i--; )
		delete m_Attributes[i];

	m_Context.ClassDtor(this);
}

const string & Class::GetName()
{
	return g_NameIdManager.IdToName(m_NameId);
}

void Class::AddBaseClass(Class *c)
{
	assert(m_State == STATE_BASE_CLASSES);
	m_BaseClasses.push_back(c);
	m_Size += c->GetSize();
}

void Class::AddAttribute( Attribute *a )
{
	assert(a && a->m_Offset == 0);
	assert(m_State <= STATE_ATTRIBUTES);
	m_State = STATE_ATTRIBUTES;

	// Padding
	{
		uint valueSize = a->GetValueSize();
		// Multiply of 4 - padding to 4 bytes
		if ((valueSize & 0x3) == 0)
		{
			if ((m_Size & 0x3) != 0)
				m_Size += 4 - (m_Size & 0x3);
		}
		// Multiply of 2 - padding to 2 bytes
		else if ((valueSize & 0x1) == 0)
		{
			if ((m_Size & 0x1) != 0)
				m_Size++;
		}
	}
	
	m_Attributes.push_back(a);
	
	a->m_Offset = m_Size;
	m_Size += a->GetSize();

	uint nameId = g_NameIdManager.NameToId(a->GetName());
	AttrVectorType::iterator byNameIter = FirstNotLessIndex(m_AttributesByNameId.begin(), m_AttributesByNameId.end(), nameId, &Attribute::NameIdCmp);
	assert(byNameIter == m_AttributesByNameId.end() || Attribute::NameIdCmp(nameId, *byNameIter) != 0);
	m_AttributesByNameId.insert(byNameIter, a);
}

bool Class::IsClass( Class *c, int *outOffset /*= NULL*/ )
{
	assert(m_State == STATE_OBJECTS);

	if (c == this)
	{
		if (outOffset)
			*outOffset = 0;
		return true;
	}

	int parentOffset = 0;
	for (uint i = 0; i < m_BaseClasses.size(); i++)
	{
		if (m_BaseClasses[i]->IsClass(c, outOffset))
		{
			if (outOffset)
				*outOffset += parentOffset;
			return true;
		}

		parentOffset += m_BaseClasses[i]->GetSize();
	}

	return false;
}

Attribute * Class::FindAttributeByName( const string &name )
{
	if (name.empty())
		return NULL;

	uint nameId = g_NameIdManager.NameToId(name);
	AttrVectorType::iterator it = BinarySearch(m_AttributesByNameId.begin(), m_AttributesByNameId.end(), nameId, &Attribute::NameIdCmp);
	if (it != m_AttributesByNameId.end())
		return *it;

	for (uint i = 0; i < m_BaseClasses.size(); i++)
	{
		Attribute *attr = m_BaseClasses[i]->FindAttributeByName(name);
		if (attr)
			return attr;
	}

	return NULL;
}

void Class::Hash()
{
	common::Hash_Calc calc;

	for (uint i = 0; i < m_BaseClasses.size(); i++)
	{
		unsigned parentHash = m_BaseClasses[i]->GetHash();
		calc.Write(&parentHash, sizeof(parentHash));
	}

	for (size_t i = 0; i < m_Attributes.size(); i++)
		m_Attributes[i]->Hash(calc);

	m_Hash = calc.Finish();
}

uint Class::GetHash()
{
	if (!m_HashCalculated)
	{
		m_State = STATE_OBJECTS;
		Hash();
	}

	return m_Hash;
}

void Class::ObjectCtor(Object *obj)
{
	assert((m_Flags & FLAG_ABSTRACT) == 0);

	m_State = STATE_OBJECTS;
	InitObject(&obj->m_Data[0]);
	m_ObjectCount++;
}

void Class::InitObject(char *ptr)
{
	int offset = 0;
	for (uint i = 0; i < m_BaseClasses.size(); i++)
	{
		m_BaseClasses[i]->InitObject(ptr + offset);
		offset += m_BaseClasses[i]->GetSize();
	}

	for (uint i = 0; i < m_Attributes.size(); i++)
		m_Attributes[i]->InitObject(ptr);
}

void Class::ObjectDtor(Object *obj)
{
	assert(m_State == STATE_OBJECTS && m_ObjectCount > 0);
	assert(&obj->GetClass() == this);
	
	m_ObjectCount--;

	FinalizeObject(&obj->m_Data[0]);
}

void Class::FinalizeObject( char *ptr )
{
	int offset = 0;
	for (uint i = 0; i < m_BaseClasses.size(); i++)
	{
		m_BaseClasses[i]->FinalizeObject(ptr + offset);
		offset += m_BaseClasses[i]->GetSize();
	}

	for (uint i = 0; i < m_Attributes.size(); i++)
		m_Attributes[i]->FinalizeObject(ptr);
}

bool Class::IsObjectValid( Object *obj, Attribute **outErrAttr, const char **outErrMsg )
{
	return ValidateObject(&obj->m_Data[0], outErrAttr, outErrMsg);
}

bool Class::ValidateObject( char *ptr, Attribute **outErrAttr, const char **outErrMsg )
{
	if (outErrAttr)
		*outErrAttr = NULL;
	if (outErrMsg)
		*outErrMsg = NULL;

	int offset = 0;
	for (uint i = 0; i < m_BaseClasses.size(); i++)
	{
		if (!m_BaseClasses[i]->ValidateObject(ptr + offset, outErrAttr, outErrMsg))
			return false;
		offset += m_BaseClasses[i]->GetSize();
	}

	for (uint i = 0; i < m_Attributes.size(); i++)
	{
		if (!m_Attributes[i]->ValidateObject(ptr, outErrMsg))
		{
			if (outErrAttr)
				*outErrAttr = m_Attributes[i];
			return false;
		}
	}
	
	return true;
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class Object

Object::Object( Class &cls )
: m_Class(cls)
, m_Data(cls.GetSize())
{
	m_Class.ObjectCtor(this);
}

Object::~Object()
{
	m_Class.ObjectDtor(this);
}

bool Object::IsValid(Attribute **outErrAttr, const char **outErrMsg)
{
	return m_Class.IsObjectValid(this, outErrAttr, outErrMsg);
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class Association

const std::string & Association::GetName()
{
	return g_NameIdManager.IdToName(m_NameId);
}

Association::Association(Context &context, const string &name, Class *cls1, ObjectAttribute *attr1)
: m_Context(context)
, m_NameId(g_NameIdManager.NameToId(name))
, m_LinkCount(0)
{
	assert(cls1 && attr1);

	m_Classes[0] = cls1;
	m_Classes[1] = NULL;
	m_Attributes[0] = attr1;
	m_Attributes[1] = NULL;

	m_Context.AssociationCtor(this);

	attr1->m_Association = this;
}

Association::~Association()
{
	m_Context.AssociationDtor(this);
}

void Association::SetClass2( Class *cls2, ObjectAttribute *attr2 )
{
	if (cls2 != m_Classes[1])
	{
		assert(m_Classes[1] == NULL && cls2 != NULL);
		m_Classes[1] = cls2;
	}
	if (attr2 != m_Attributes[1])
	{
		assert(m_Attributes[1] == NULL && attr2 != NULL);
		m_Attributes[1] = attr2;
		attr2->m_Association = this;
	}
}

void Association::SetClassName2( const string &className2 )
{
	if (className2 != m_ClassName2)
	{
		assert(m_ClassName2.empty() && !className2.empty());
		m_ClassName2 = className2;
	}
}

bool Association::TryLinkObjects( Object *obj1, Object *obj2 )
{
	assert(m_Classes[0] && m_Classes[1] && m_Attributes[0] && m_Attributes[1]);
	assert(obj1->GetClass().IsClass(m_Classes[0]));
	assert(obj2->GetClass().IsClass(m_Classes[1]));

	if (LinkExists(obj1, obj2))
		return false;

	uint valCount1 = m_Attributes[0]->GetValueCount(obj1);
	uint valCount2 = m_Attributes[1]->GetValueCount(obj2);

	// No space for another link
	if (valCount1 >= m_Attributes[0]->GetMaxCount() && *m_Attributes[0]->AccessValueT(obj1, valCount1-1))
		return false;
	if (valCount2 >= m_Attributes[1]->GetMaxCount() && *m_Attributes[1]->AccessValueT(obj2, valCount2-1))
		return false;

	// Perform link

	if (*m_Attributes[0]->AccessValueT(obj1, valCount1-1) == NULL)
		*m_Attributes[0]->AccessValueT(obj1, valCount1-1) = obj2;
	else
		m_Attributes[0]->AddValue(obj1, obj2);

	if (*m_Attributes[1]->AccessValueT(obj2, valCount2-1) == NULL)
		*m_Attributes[1]->AccessValueT(obj2, valCount2-1) = obj1;
	else
		m_Attributes[1]->AddValue(obj2, obj1);

	m_LinkCount++;

	return true;
}

bool Association::LinkExists( Object *obj1, Object *obj2 )
{
	assert(m_Classes[0] && m_Classes[1] && m_Attributes[0] && m_Attributes[1]);
	assert(obj1->GetClass().IsClass(m_Classes[0]));
	assert(obj2->GetClass().IsClass(m_Classes[1]));
	
	if (m_LinkCount == 0)
		return false;

	uint valCount1 = m_Attributes[0]->GetValueCount(obj1);
	uint valCount2 = m_Attributes[1]->GetValueCount(obj2);

	if (valCount1 < valCount2)
		return m_Attributes[0]->LinkExists(obj1, obj2);
	else
		return m_Attributes[1]->LinkExists(obj2, obj1);
}

void Association::LinkObjects( Object *obj1, Object *obj2 )
{
	if (!TryLinkObjects(obj1, obj2))
		throw Error("Cannot link objects.");
}

bool Association::TryUnlinkObjects( Object *obj1, Object *obj2 )
{
	assert(m_Classes[0] && m_Classes[1] && m_Attributes[0] && m_Attributes[1]);
	assert(obj1->GetClass().IsClass(m_Classes[0]));
	assert(obj2->GetClass().IsClass(m_Classes[1]));

	if (m_LinkCount == 0)
		return false;

	uint index1 = m_Attributes[0]->FindValue(obj1, obj2);
	if (index1 == MAXUINT4)
		return false;

	uint index2 = m_Attributes[1]->FindValue(obj2, obj1);
	if (index2 == MAXUINT4)
		return false;

	uint valCount1 = m_Attributes[0]->GetValueCount(obj1);
	uint valCount2 = m_Attributes[1]->GetValueCount(obj2);
	
	// Perform unlink

	if (valCount1 <= m_Attributes[0]->GetMinCount())
		*m_Attributes[0]->AccessValueT(obj1, index1) = NULL;
	else
		m_Attributes[0]->RemoveValue(obj1, index1);

	if (valCount2 <= m_Attributes[1]->GetMinCount())
		*m_Attributes[1]->AccessValueT(obj2, index2) = NULL;
	else
		m_Attributes[1]->RemoveValue(obj2, index2);

	m_LinkCount--;

	return true;
}

void Association::UnlinkObjects( Object *obj1, Object *obj2 )
{
	if (!TryUnlinkObjects(obj1, obj2))
		throw Error("Cannot unlink objects.");
}

//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class Context

Context::Context()
: m_InsideDestructor(false)
{

}

Context::~Context()
{
	m_InsideDestructor = true;

	for (uint i = m_Associations.size(); i--; )
		delete m_Associations[i];
	for (uint i = m_Classes.size(); i--; )
		delete m_Classes[i];
}

Class * Context::FindClassByName( const string &name )
{
	if (name.empty())
		return NULL;
	uint nameId = g_NameIdManager.NameToId(name);
	ClassVector::iterator it = BinarySearch(m_Classes.begin(), m_Classes.end(), nameId, &Class::NameIdCmp);
	if (it != m_Classes.end())
		return *it;
	return NULL;
}

void Context::ClassCtor( Class *cls )
{
	ClassVector::iterator byNameIter = FirstNotLessIndex(m_Classes.begin(), m_Classes.end(), cls->GetNameId(), &Class::NameIdCmp);
	assert(byNameIter == m_Classes.end() || Class::NameIdCmp(cls->GetNameId(), *byNameIter) != 0);
	m_Classes.insert(byNameIter, cls);
}

void Context::ClassDtor( Class *cls )
{
	if (m_InsideDestructor)
		return;

	ClassVector::iterator it = BinarySearch(m_Classes.begin(), m_Classes.end(), cls->GetNameId(), &Class::NameIdCmp);
	assert(it != m_Classes.end());
	m_Classes.erase(it);
}

void Context::LoadClasses( common::Stream &s)
{
	Tokenizer tokenizer(&s, Tokenizer::FLAG_MULTILINE_STRINGS);
	tokenizer.Next();
	while (!tokenizer.QueryEOF())
	{
		if (tokenizer.QueryIdentifier("class"))
		{
			tokenizer.Next();
			ParseClass(tokenizer);
		}
		else
			tokenizer.CreateError("\"class\" expected.");
	}

	LinkAssociations();
}

void Context::ParseClass(common::Tokenizer &tok)
{
	string name;
	ParseName(name, tok);

	std::vector<Class*> baseClasses;

	uint flags = 0;
	while (tok.QueryToken(Tokenizer::TOKEN_IDENTIFIER))
	{
		if (tok.GetString() == "abstract")
		{
			tok.Next();
			flags |= Class::FLAG_ABSTRACT;
		}
		else if (tok.GetString() == "inherits")
		{
			string baseName;
			do
			{
				tok.Next();

				ParseName(baseName, tok);
				Class *baseClass = FindClassByName(baseName);
				if (baseClass == NULL)
					tok.CreateError(Format("Base class \"#\" not found.") % baseName);
				baseClasses.push_back(baseClass);
			}
			while (tok.QuerySymbol(','));
		}
	}

	Class *cls = new Class(*this, name, flags);
	for (uint i = 0; i < baseClasses.size(); i++)
		cls->AddBaseClass(baseClasses[i]);

	tok.AssertSymbol('{');
	tok.Next();

	while (!tok.QuerySymbol('}'))
		ParseClassEntry(tok, *cls);
	tok.Next();
}

void Context::ParseClassEntry(common::Tokenizer &tok, Class &cls)
{
	string attrName, attrTypeName;
	ParseName(attrTypeName, tok);
	ParseName(attrName, tok);

	ATTR_TYPE attrType = ATTR_TYPE_NULL;
	for (uint i = 0; i < ATTR_TYPE_COUNT; i++)
	{
		if (attrTypeName == ATTR_TYPE_NAMES[i])
		{
			attrType = (ATTR_TYPE)i;
			break;
		}
	}
	if (attrType == ATTR_TYPE_NULL)
	{
		for (uint i = 0; i < _countof(ATTR_ALT_NAMES); i++)
		{
			if (attrTypeName == ATTR_ALT_NAMES[i].AltName)
			{
				attrType = ATTR_ALT_NAMES[i].Type;
				break;
			}
		}
	}
	// Not an intrinsic type - probably class type.
	if (attrType == ATTR_TYPE_NULL)
		attrType = ATTR_TYPE_OBJECT;

	uint minCount, maxCount;
	ParseValueCount(minCount, maxCount, tok);

	uint attrFlags = 0;
	if (maxCount > 0xffff || maxCount > 1 && maxCount != minCount)
		attrFlags |= Attribute::FLAG_DYNAMIC;

	while (tok.QueryToken(Tokenizer::TOKEN_IDENTIFIER))
	{
		if (tok.GetString() == "invisible")
			attrFlags |= Attribute::FLAG_INVISIBLE;
		else if (tok.GetString() == "readonly")
			attrFlags |= Attribute::FLAG_READ_ONLY;
		else if (tok.GetString() == "static")
			attrFlags &= ~Attribute::FLAG_DYNAMIC;
		else if (tok.GetString() == "dynamic")
			attrFlags |= Attribute::FLAG_DYNAMIC;
		else if (tok.GetString() == "key")
			attrFlags |= Attribute::FLAG_KEY;
		else if (tok.GetString() == "auto")
			attrFlags |= Attribute::FLAG_AUTO;
		else
			tok.CreateError("Invalid attribute flag.");
		
		tok.Next();
	}

	Attribute *attr = CreateAttribute(cls, tok, attrType, attrName, minCount, maxCount, attrFlags, attrTypeName);
	cls.AddAttribute(attr);

	tok.AssertSymbol(';');
	tok.Next();
}

void Context::ParseName(std::string &out, common::Tokenizer &tok)
{
	tok.AssertToken(Tokenizer::TOKEN_IDENTIFIER, Tokenizer::TOKEN_STRING);
	out = tok.GetString();
	tok.Next();
}

void Context::ParseValueCount( uint &outMinCount, uint &outMaxCount, common::Tokenizer &tok )
{
	if (tok.QuerySymbol('['))
	{
		tok.Next();
		if (tok.QuerySymbol(']'))
		{
			tok.Next();
			outMinCount = 0;
			outMaxCount = MAXUINT4;
		}
		else
		{
			if (tok.QuerySymbol('?'))
			{
				tok.Next();
				outMinCount = 0;
				outMaxCount = 1;
			}
			else if (tok.QuerySymbol('*'))
			{
				tok.Next();
				outMinCount = 0;
				outMaxCount = MAXUINT4;
			}
			else if (tok.QuerySymbol('+'))
			{
				tok.Next();
				outMinCount = 1;
				outMaxCount = MAXUINT4;
			}
			else if (tok.QueryToken(Tokenizer::TOKEN_INTEGER))
			{
				outMinCount = tok.MustGetUint4();
				tok.Next();
				if (tok.QuerySymbol('.'))
				{
					tok.Next();
					tok.AssertSymbol('.');
					tok.Next();
					if (tok.QueryToken(Tokenizer::TOKEN_INTEGER))
					{
						outMaxCount = tok.MustGetUint4();
						tok.Next();
					}
					else if (tok.QueryIdentifier("inf"))
					{
						tok.Next();
						outMaxCount = MAXUINT4;
					}
					else
						tok.CreateError("Expected maximum value count or \"inf\".");
				}
				else
					outMaxCount = outMinCount;
			}
			else
				tok.CreateError("Invalid value count spefifier.");

			tok.AssertSymbol(']');
			tok.Next();
		}
	}
	else
		outMinCount = outMaxCount = 1;

	if (outMaxCount < outMinCount)
		tok.CreateError("Maximum value count must be greater or equal than minimum value count.");
}

Attribute * Context::CreateAttribute( Class &cls, common::Tokenizer &tok, ATTR_TYPE type, const string &name, uint minCount, uint maxCount, uint flags, const string &typeName )
{
	switch (type)
	{
	case ATTR_TYPE_OBJECT:
		return ObjectAttribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_CHAR:
		return CharAttribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_STRING:
		return StringAttribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_BOOL:
		return BoolAttribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	
	case ATTR_TYPE_UINT8:
		return Uint8Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_UINT16:
		return Uint16Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_UINT32:
		return Uint32Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_UINT64:
		return Uint64Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_INT8:
		return Int8Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_INT16:
		return Int16Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_INT32:
		return Int32Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_INT64:
		return Int64Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	
	case ATTR_TYPE_FLOAT32:
		return Float32Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);
	case ATTR_TYPE_FLOAT64:
		return Float64Attribute::Create(*this, cls, tok, name, minCount, maxCount, flags, typeName);

	default:
		assert(0);
		return NULL;
	}
}

uint Context::CalcSumObjectCount()
{
	uint objCount = 0;
	for (uint i = 0; i < m_Classes.size(); i++)
		objCount = m_Classes[i]->GetObjectCount();
	return objCount;
}

void Context::LinkAssociations()
{
	for (uint i = 0; i < m_Associations.size(); i++)
	{
		Association &assoc = *m_Associations[i];
		assert((assoc.m_Classes[1] == NULL) == (assoc.m_Attributes[1] == NULL));
		
		if (assoc.m_Classes[1] == NULL)
		{
			Class *cls = FindClassByName(assoc.m_ClassName2);
			if (cls == NULL)
				throw Error(Format("Cannot link class association \"#\": Cannot find class \"#\"") % assoc.GetName() % assoc.m_ClassName2);
			ObjectAttribute *attr = new ObjectAttribute(string(), 0, 1, Attribute::FLAG_INVISIBLE);
			cls->AddAttribute(attr);
			assoc.SetClass2(cls, attr);
		}

		if (!assoc.m_ClassName2.empty())
			assoc.m_ClassName2.swap(string());
	}
}

void Context::AssociationCtor( Association *assoc )
{
	m_Associations.push_back(assoc);
}

void Context::AssociationDtor( Association *assoc )
{
	if (m_InsideDestructor)
		return;

	AssociationVector::iterator it = std::find(m_Associations.begin(), m_Associations.end(), assoc);
	assert(it != m_Associations.end());
	m_Associations.erase(it);
}

Association * Context::FindAssociationByName( const string &name )
{
	if (name.empty())
		return NULL;

	uint nameId = g_NameIdManager.NameToId(name);

	for (uint i = 0; i < m_Associations.size(); i++)
		if (m_Associations[i]->GetNameId() == nameId)
			return m_Associations[i];
	return NULL;
}

//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class ObjectAttribute

ObjectAttribute * ObjectAttribute::Create(Context &ctx, Class &cls, common::Tokenizer &tok, const string &name, uint minCount, uint maxCount, uint flags, const string &typeName)
{
	string assocName;

	if (tok.QuerySymbol('{'))
	{
		tok.Next();

		while (!tok.QuerySymbol('}'))
		{
			tok.AssertToken(Tokenizer::TOKEN_IDENTIFIER);
			if (tok.GetString() == "Name")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				Context::ParseName(assocName, tok);
			}
			else
				tok.CreateError("Invalid object attribute parameter.");
		}
		tok.Next();
	}

	ObjectAttribute *attr = new ObjectAttribute(name, minCount, maxCount, flags);
	
	Association *assoc = ctx.FindAssociationByName(assocName);
	if (assoc)
		assoc->SetClass2(&cls, attr);
	else
	{
		assoc = new Association(ctx, assocName, &cls, attr);
		assoc->SetClassName2(typeName);
	}
	
	return attr;
}

ObjectAttribute::ObjectAttribute( const string &name, uint minCount, uint maxCount, uint flags )
: AttributeT(name, minCount, maxCount, flags)
, m_Association(NULL)
{

}

bool ObjectAttribute::ValidateValue( char *ptr, const char **outErrMsg )
{
	assert(m_Association);

	Object *val = *(Object**)ptr;
	if (val == NULL)
	{
		if (outErrMsg)
			*outErrMsg = "Object reference is null.";
		return false;
	}
	else
	{
		if (outErrMsg)
			*outErrMsg = NULL;
		return true;
	}
}

bool ObjectAttribute::LinkExists( Object *thisObj, Object *thatObj )
{
	uint valCount = GetValueCount(thisObj);
	for (uint i = 0; i < valCount; i++)
		if (*AccessValueT(thisObj, i) == thatObj)
			return true;
	return false;
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class CharAttribute

CharAttribute * CharAttribute::Create( Context &ctx, Class &cls, common::Tokenizer &tok, const string &name, uint minCount, uint maxCount, uint flags, const string &typeName )
{
	uint constraints = 0;

	if (tok.QuerySymbol('{'))
	{
		tok.Next();

		while (!tok.QuerySymbol('}'))
		{
			uint constraint = ParseConstraintIdentifier(tok);
			if (constraint == 0)
				tok.CreateError("Invalid character attribute constraint.");
			constraints |= constraint;

			if (!tok.QuerySymbol('}'))
			{
				tok.AssertSymbol(',');
				tok.Next();
			}
		}
		tok.Next();
	}

	CharAttribute *attr = new CharAttribute(name, minCount, maxCount, flags);
	attr->Constraints = constraints;
	return attr;
}

CharAttribute::CharAttribute( const string &name, uint minCount, uint maxCount, uint flags )
: AttributeT<char>(name, minCount, maxCount, flags)
, Constraints(0)
{
}

bool CharAttribute::ValidateValue( char *ptr, const char **outErrMsg )
{
	return ValidateChar(*ptr, Constraints, outErrMsg);
}

bool CharAttribute::ValidateChar( char ch, uint constraints, const char **outErrMsg )
{
	if (outErrMsg)
		*outErrMsg = NULL;

	if (constraints == 0)
		return true;

	if (constraints & CONSTRAINT_NO_ZERO)
	{
		if (ch == 0)
		{
			if (outErrMsg)
				*outErrMsg = "Character is zero.";
			return false;
		}
	}
	if (constraints & CONSTRAINT_NO_EOL)
	{
		if (ch == '\n' || ch == '\r')
		{
			if (outErrMsg)
				*outErrMsg = "Character is end of line.";
			return false;
		}
	}
	if (constraints & CONSTRAINT_ANSI_ONLY)
	{
		if ((unsigned char)ch > 127)
		{
			if (outErrMsg)
				*outErrMsg = "Character is not ANSI.";
			return false;
		}
	}
	if (constraints & CONSTRAINT_NO_SPECIAL)
	{
		if ((unsigned char)ch < 32)
		{
			if (outErrMsg)
				*outErrMsg = "Character is special.";
			return false;
		}
	}

	if (constraints & (CONSTRAINT_DIGIT | CONSTRAINT_HEX_DIGIT | CONSTRAINT_ALPHA | CONSTRAINT_ALPHANUMERIC | CONSTRAINT_WHITE))
	{
		if (constraints & CONSTRAINT_ALPHANUMERIC)
			if (CharIsAlphaNumeric(ch))
				return true;
		if (constraints & CONSTRAINT_ALPHA)
			if (CharIsAlpha(ch))
				return true;
		if (constraints & CONSTRAINT_HEX_DIGIT)
			if (CharIsHexDigit(ch))
				return true;
		if (constraints & CONSTRAINT_DIGIT)
			if (CharIsDigit(ch))
				return true;
		if (constraints & CONSTRAINT_WHITE)
			if (CharIsWhitespace(ch))
				return true;
		
		if (outErrMsg)
			*outErrMsg = "Character does not belong to allowed groups.";
		return false;
	}

	return true;
}

uint CharAttribute::ParseConstraintIdentifier( common::Tokenizer &tok )
{
	uint constraint = 0;

	if (tok.QueryToken(Tokenizer::TOKEN_IDENTIFIER))
	{
		if (tok.GetString() == "Digit")
			constraint = CONSTRAINT_DIGIT;
		else if (tok.GetString() == "HexDigit")
			constraint = CONSTRAINT_HEX_DIGIT;
		else if (tok.GetString() == "Alpha")
			constraint = CONSTRAINT_ALPHA;
		else if (tok.GetString() == "AlphaNumeric")
			constraint = CONSTRAINT_ALPHANUMERIC;
		else if (tok.GetString() == "White")
			constraint = CONSTRAINT_WHITE;
		else if (tok.GetString() == "NoEol")
			constraint = CONSTRAINT_NO_EOL;
		else if (tok.GetString() == "AnsiOnly")
			constraint = CONSTRAINT_ANSI_ONLY;
		else if (tok.GetString() == "NoSpecial")
			constraint = CONSTRAINT_NO_SPECIAL;
		else if (tok.GetString() == "NoZero")
			constraint = CONSTRAINT_NO_ZERO;
	}

	if (constraint)
		tok.Next();

	return constraint;
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class StringAttribute 

StringAttribute * StringAttribute::Create( Context &ctx, Class &cls, common::Tokenizer &tok, const string &name, uint minCount, uint maxCount, uint flags, const string &typeName )
{
	uint charConstraints = 0, minLen = 0, maxLen = MAXUINT4;
	bool isPassword = false;

	if (tok.QuerySymbol('{'))
	{
		tok.Next();

		while (!tok.QuerySymbol('}'))
		{
			bool done = false;
			if (tok.QueryToken(Tokenizer::TOKEN_IDENTIFIER))
			{
				done = true;

				if (tok.GetString() == "MinLength")
				{
					tok.Next();
					tok.AssertSymbol('=');
					tok.Next();
					minLen = tok.MustGetUint4();
					tok.Next();
				}
				else if (tok.GetString() == "MaxLength")
				{
					tok.Next();
					tok.AssertSymbol('=');
					tok.Next();
					if (tok.QueryIdentifier("inf"))
						maxLen = MAXUINT4;
					else
						maxLen = tok.MustGetUint4();
					tok.Next();
				}
				else if (tok.GetString() == "Password")
				{
					tok.Next();
					isPassword = true;
				}
				else if (tok.GetString() == "NotEmpty")
				{
					tok.Next();
					if (minLen == 0)
						minLen = 1;
				}
				/*else if (tok.GetString() == "SingleLine")
				{
					tok.Next();
					charConstraints |= CharAttribute::CONSTRAINT_NO_EOL;
				}*/
				else
					done = false;
			}

			if (!done)
			{
				uint constraint = CharAttribute::ParseConstraintIdentifier(tok);
				if (constraint)
				{
					charConstraints |= constraint;
					done = true;
				}
			}

			if (!done)
				tok.CreateError("Invalid string attribute constraint.");

			if (!tok.QuerySymbol('}'))
			{
				tok.AssertSymbol(',');
				tok.Next();
			}
		}
		tok.Next();
	}

	if (maxLen < minLen)
		tok.CreateError("Maximum string length must be greater or equal than minimum length.");

	StringAttribute *attr = new StringAttribute(name, minCount, maxCount, flags);
	attr->CharConstraints = charConstraints;
	attr->MinLength = minLen;
	attr->MaxLength = maxLen;
	attr->IsPassword = isPassword;
	return attr;
}

StringAttribute::StringAttribute( const string &name, uint minCount, uint maxCount, uint flags )
: AttributeT<string>(name, minCount, maxCount, flags)
, CharConstraints(0)
, MinLength(0)
, MaxLength(MAXUINT4)
, IsPassword(false)
{

}

bool StringAttribute::ValidateValue( char *ptr, const char **outErrMsg )
{
	if (outErrMsg)
		*outErrMsg = NULL;

	const string &val = *(const string*)ptr;
	
	if (val.length() < MinLength)
	{
		if (outErrMsg)
			*outErrMsg = "String is too short.";
		return false;
	}
	if (val.length() > MaxLength)
	{
		if (outErrMsg)
			*outErrMsg = "String is too long.";
		return false;
	}

	for (uint i = 0; i < val.length(); i++)
		if (!CharAttribute::ValidateChar(val[i], CharConstraints, outErrMsg))
			return false;

	return true;
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// class BoolAttribute

BoolAttribute * BoolAttribute::Create( Context &ctx, Class &cls, common::Tokenizer &tok, const string &name, uint minCount, uint maxCount, uint flags, const string &typeName )
{
	return new BoolAttribute(name, minCount, maxCount, flags);
}

BoolAttribute::BoolAttribute( const string &name, uint minCount, uint maxCount, uint flags )
: AttributeT<bool>(name, minCount, maxCount, flags)
{

}

} // namespace paramframe

#endif // #if 0
